#pragma once
#include "util.h"

namespace banknamespace
{
	struct mat16f
	{
	public:
		float data[16];
		explicit mat16f() 
		{
			/*for (int i = 0; i < 16; ++i)
			{
				data[i] = 0.0f;
			}*/
		}
		explicit mat16f( float mat[16] ) 
		{
			memcpy( data, mat, sizeof( float ) * 16 );
		}

		void matrixTranspose(float* mat)
		{
			//in-place transpose
			std::swap(mat[1],mat[4]);
			std::swap(mat[2],mat[8]);
			std::swap(mat[3],mat[12]);
			std::swap(mat[6],mat[9]);
			std::swap(mat[7],mat[13]);
			std::swap(mat[11],mat[14]);
		}

		void matrixMult(const float* lhs, const float* rhs, float* result)
		{
			//assume that the arguments are valid float[16] buffers
			for (int i = 0; i < 16; i += 4)
			{
				result[i+0] = lhs[i+0]*rhs[0] + lhs[i+1]*rhs[4] + lhs[i+2]*rhs[8] + lhs[i+3]*rhs[12];
				result[i+1] = lhs[i+0]*rhs[1] + lhs[i+1]*rhs[5] + lhs[i+2]*rhs[9] + lhs[i+3]*rhs[13];
				result[i+2] = lhs[i+0]*rhs[2] + lhs[i+1]*rhs[6] + lhs[i+2]*rhs[10] + lhs[i+3]*rhs[14];
				result[i+3] = lhs[i+0]*rhs[3] + lhs[i+1]*rhs[7] + lhs[i+2]*rhs[11] + lhs[i+3]*rhs[15];
			}
			
			/*result[0] = lhs[0]*rhs[0] + lhs[1]*rhs[4] + lhs[2]*rhs[8] + lhs[3]*rhs[12];
			result[1] = lhs[0]*rhs[1] + lhs[1]*rhs[5] + lhs[2]*rhs[9] + lhs[3]*rhs[13];
			result[2] = lhs[0]*rhs[2] + lhs[1]*rhs[6] + lhs[2]*rhs[10] + lhs[3]*rhs[14];
			result[3] = lhs[0]*rhs[3] + lhs[1]*rhs[7] + lhs[2]*rhs[11] + lhs[3]*rhs[15];

			result[4] = lhs[4]*rhs[0] + lhs[5]*rhs[4] + lhs[6]*rhs[8] + lhs[7]*rhs[12];
			result[5] = lhs[4]*rhs[1] + lhs[5]*rhs[5] + lhs[6]*rhs[9] + lhs[7]*rhs[13];
			result[6] = lhs[4]*rhs[2] + lhs[5]*rhs[6] + lhs[6]*rhs[10] + lhs[7]*rhs[14];
			result[7] = lhs[4]*rhs[3] + lhs[5]*rhs[7] + lhs[6]*rhs[11] + lhs[7]*rhs[15];

			result[8] = lhs[8]*rhs[0] + lhs[9]*rhs[4] + lhs[10]*rhs[8] + lhs[11]*rhs[12];
			result[9] = lhs[8]*rhs[1] + lhs[9]*rhs[5] + lhs[10]*rhs[9] + lhs[11]*rhs[13];
			result[10] = lhs[8]*rhs[2] + lhs[9]*rhs[6] + lhs[10]*rhs[10] + lhs[11]*rhs[14];
			result[11] = lhs[8]*rhs[3] + lhs[9]*rhs[7] + lhs[10]*rhs[11] + lhs[11]*rhs[15];

			result[12] = lhs[12]*rhs[0] + lhs[13]*rhs[4] + lhs[14]*rhs[8] + lhs[15]*rhs[12];
			result[13] = lhs[12]*rhs[1] + lhs[13]*rhs[5] + lhs[14]*rhs[9] + lhs[15]*rhs[13];
			result[14] = lhs[12]*rhs[2] + lhs[13]*rhs[6] + lhs[14]*rhs[10] + lhs[15]*rhs[14];
			result[15] = lhs[12]*rhs[3] + lhs[13]*rhs[7] + lhs[14]*rhs[11] + lhs[15]*rhs[15];*/
		}

		void transform( float* num )
		{
			float tmp[3];
			memcpy( tmp, num, sizeof( float ) * 3 );

			num[0] = data[0]*tmp[0] + data[4]*tmp[1] + data[8]*tmp[2];
			num[1] = data[1]*tmp[0] + data[5]*tmp[1] + data[9]*tmp[2];
			num[2] = data[2]*tmp[0] + data[6]*tmp[1] + data[10]*tmp[2];
		}
	
		void transform2( float* num )
		{
			float tmp[4];
			memcpy( tmp, num, sizeof( float ) * 4 );

			num[0] = data[0]*tmp[0] + data[4]*tmp[1] + data[8]*tmp[2] + data[12]*tmp[3];
			num[1] = data[1]*tmp[0] + data[5]*tmp[1] + data[9]*tmp[2] + data[13]*tmp[3];
			num[2] = data[2]*tmp[0] + data[6]*tmp[1] + data[10]*tmp[2] + data[14]*tmp[3];
			num[3] = data[3]*tmp[0] + data[7]*tmp[1] + data[11]*tmp[2] + data[15]*tmp[3];
		}

		void transpose()
		{
			matrixTranspose( data );
		}

		mat16f& operator+=( const mat16f& other )
		{
			for (int i = 0; i < 16; ++i)
			{
				data[i] += other.data[i];
			}

			return *this;
		}

		mat16f operator+( mat16f& other ) const
		{
			mat16f result( *this );
			result += other;
			return result;
		}

		mat16f& operator-=( const mat16f& other )
		{
			for (int i = 0; i < 16; ++i)
			{
				data[i] -= other.data[i];
			}

			return *this;
		}

		mat16f operator-( mat16f& other ) const
		{
			mat16f result( *this );
			result -= other;
			return result;
		}

		mat16f& operator*=( const mat16f& other )
		{
			float tmp[16];
			memcpy( tmp, data, sizeof( float ) * 16 );

			matrixMult( tmp, other.data, data );
			return *this;
		}

		mat16f operator*( mat16f& other ) const
		{
			mat16f result( *this );
			result *= other;
			return result;
		}

		bool operator==( const mat16f& other )
		{
			bool res = true;

			for (int i = 0; i < 16; ++i)
			{
				if (data[i] != other.data[i])
				{
					res = false;
					break;
				}
			}
			return res;
		}
	};

}
